#ifndef GRAPH_H_
#define GRAPH_H_

#include <string>
#include "Vertex.h"
#include "GraphVisitor.h"
#include "core/exceptions/DuplicateElementException.h"
#include "core/path/PathSolverAlgorithm.h"
#include "core/path/Path.h"
#include "util/exceptions/ElementNotFoundException.h"
#include "util/LinkedList.h"

using namespace std;

/*
 * Clase que representa al grafo. Contiene sus aristas y vertices.
 */
enum TransverseStrategy {
	DepthFirst, // Recorrido en profundida
	BreadthFirs
// Recorrido en anchura
};

class Graph {
public:

	/*
	 * Constructor por defecto.
	 */
	Graph();

	/*
	 * Destructor.
	 */
	virtual ~Graph();

	/*
	 * Metodo para agregar un vertice al grafo. Este devuelve una referencia al vertice que se agrega.
	 * Devuelve DuplicateElementException en caso que ya exista el vertice.
	 */
	virtual Vertex* addVertex(string id, double position_x, double position_y)
			throw (DuplicateElementException);

	/*
	 * Metodo para agregar una arista al grafo. Este devuelve una referencia a la arista que se agrega.
	 * Devuelve DuplicateElementException en caso que ya exista la arista.
	 */
	virtual Edge* addEdge(string id, Vertex* origin,
			Vertex* destination, double distanceInMeters, double speedInKMH)
			throw (DuplicateElementException);

	/*
	 * Utiliza el solver para resolver un camino que ese objeto considere entre el origen y el destino.
	 */
	virtual path::Path* solvePath(path::PathSolverAlgorithm &solver,
			Vertex* origin, Vertex* destination)  throw (OutOfReachException);

	/*
	 * Ejecuta en el visitor recibido el metodo visitVertex una vez por cada nodo
	 * dentro del grafo.
	 */
	virtual void visitVertices(GraphVisitor &aVisitor);

	/*
	 * Ejecuta en el visitor recibido el metodo visitEdge una vez por cada arista
	 * dentro del grafo.
	 */
	virtual void visitEdges(GraphVisitor &aVisitor);

	/*
	 * Recorre el grafo a partir del vertice recibido. Utilizando la estrategia indicada.
	 * Por cada vertice que se visite se ejecutara, sobre aVisitor, el metodo visitVertex
	 * Por cada arista que se utilice para llegar a los nuevos vertices se ejecutara visitEdge
	 * TODO No implementado porque por ahora no se necesita
	 */
	virtual void transverseGraph(GraphVisitor &aVisitor, Vertex* origin,
			TransverseStrategy strategy) {
	}

	/*
	 * Recupera un vertice por id. Devuelve ElementNotFoundException en caso de no existir
	 * el vertice en el grafo.
	 */
	virtual Vertex* getVertex(string id) throw (util::ElementNotFoundException);

	/*
	 * Recupera una arista por id. Devuelve ElementNotFoundException en caso de no existir
	 * la arista en el grafo.
	 */
	virtual Edge* getEdge(string id)
			throw (util::ElementNotFoundException);

	/*
	 * Indici si el grafo esta vacio.
	 */
	virtual bool isEmpty();

private:
	util::LinkedList<Vertex*> vertices;
	util::LinkedList<Edge*> edges;
};

#endif /* GRAPH_H_ */
